<template>
  <div style="width: 100%; height: 100%">
    <div id="threejs-container"></div>
    <div id="threejs-contextMenu" v-if="contextMenuVisible">
      <div class="item" v-for="(item, key) in contextMenu" :key="key" @click="clickcontextMenuHandle(item.methods)">
        {{ item.name }}
      </div>
    </div>
  </div>
</template>

<script>
import * as THREE from 'three';
// 引入扩展库OrbitControls.js
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
import { DragControls } from './DragControls.js';

import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';

import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass';
import { OutlinePass } from 'three/examples/jsm/postprocessing/OutlinePass';
import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer';
const ThreeBSP = require('three-js-csg')(THREE);
export default {
  props: {
    edit: {
      type: Boolean,
      default: false
    },
    contextMenu: {
      type: Array,
      default: () => {
        return [];
      }
    }
  },
  data() {
    return {
      scene: null,
      camera: null,
      renderer: null,
      controls: null,
      container: null,
      VIEW_ANGLE: 75,
      SCREEN_WIDTH: null,
      SCREEN_HEIGHT: null,
      ASPECT: null,
      NEAR: 0.1,
      FAR: 10000,
      animate: null,
      dragControls: null,
      groupList: [],
      selectedOutLine: null,
      isRotating: false,
      contextMenuVisible: false,
      currentObj: null
    };
  },
  mounted() {
    this.init();
    this.startAnimate();
  },
  methods: {
    clickcontextMenuHandle(cmd) {
      this.contextMenuVisible = false;
      this.$emit(cmd, this.currentObj);
    },
    init() {
      this.container = document.getElementById('threejs-container');
      this.SCREEN_WIDTH = this.container.clientWidth;
      this.SCREEN_HEIGHT = this.container.clientHeight;
      this.ASPECT = this.SCREEN_WIDTH / this.SCREEN_HEIGHT;
      this.initScene();
      this.initCamera();
      this.initRender();
      this.initControls();
      this.initLight();
      this.initBasic();
      if (this.edit) {
        this.initDrag();
      }
      this.initClick();
    },
    initScene() {
      this.scene = new THREE.Scene();
    },
    initCamera() {
      this.camera = new THREE.PerspectiveCamera(this.VIEW_ANGLE, this.ASPECT, this.NEAR, this.FAR);
      this.camera.position.set(150, 200, 100);
      this.camera.lookAt(this.scene.position);
      this.scene.add(this.camera);
    },
    initRender() {
      this.renderer = new THREE.WebGLRenderer({ antialias: true });
      this.renderer.setSize(this.SCREEN_WIDTH, this.SCREEN_HEIGHT);
      this.renderer.shadowMap.enabled = true; // 显示阴影
      this.container.appendChild(this.renderer.domElement);
      this.renderer.setClearColor(0xffffff, 1);

      const defaultBackground = new THREE.Color(0x000000);
      this.scene.background = defaultBackground;

      // 创建立体网格背景
      if (this.edit) {
        this.createGrid(500, 30, new THREE.Vector3(0, 2, 0), new THREE.Euler(0, 0, 0));
      }
    },
    initControls() {
      this.controls = new OrbitControls(this.camera, this.renderer.domElement);
    },
    initLight() {
      let directionalLight = new THREE.DirectionalLight(0xffffff);
      directionalLight.position.set(-120, 160, -60);
      directionalLight.intensity = 0.5;
      directionalLight.castShadow = true;
      directionalLight.shadow.mapSize.width = 2048;
      directionalLight.shadow.mapSize.height = 2048;
      directionalLight.shadow.camera.near = 0.5;
      directionalLight.shadow.camera.far = 500;
      directionalLight.shadow.camera.left = -200;
      directionalLight.shadow.camera.right = 200;
      directionalLight.shadow.camera.top = 200;
      directionalLight.shadow.camera.bottom = -200;
      directionalLight.shadow.bias = -0.002;
      this.scene.add(directionalLight);

      var ambient = new THREE.AmbientLight(0xffffff, 1);
      ambient.position.set(0, 0, 0);
      this.scene.add(ambient);
    },
    createGrid(size, divisions, position, rotation) {
      const gridHelper = new THREE.GridHelper(size, divisions);
      gridHelper.position.copy(position);
      gridHelper.rotation.set(rotation.x, rotation.y, rotation.z);
      this.scene.add(gridHelper);
    },
    initBasic() {
      this.createFloor();
      this.createWall(2, 50, 150, 0, -90, 25, 0);
      this.createWall(2, 50, 150, 1, 90, 25, 0);
      this.createWall(2, 50, 180, 1.5, 0, 25, -75);
      this.createWall(2, 50, 180, 1.5, 0, 25, 75);
    },
    initDrag() {
      this.dragControls = new DragControls(this.groupList, this.camera, this.renderer.domElement);
      let oldY = null;
      this.dragControls.transformGroup = true;
      this.dragControls.addEventListener('dragstart', (event) => {
        this.controls.enabled = false;
        oldY = event.object.position.y;
        event.object = null;
      });

      this.dragControls.addEventListener('drag', (event) => {
        this.controls.enabled = false;
        if (event.object.position.y != oldY) {
          event.object.position.y = oldY;
        }
      });

      this.dragControls.addEventListener('dragend', (event) => {
        this.controls.enabled = true;
      });
    },
    initClick() {
      let startX, startY;

      this.renderer.domElement.addEventListener('mousedown', (event) => {
        this.contextMenuVisible = false;
        startX = event.clientX;
        startY = event.clientY;
        this.isRotating = true;
      });

      this.renderer.domElement.addEventListener('mouseup', (event) => {
        this.isRotating = false;
        const dx = event.clientX - startX;
        const dy = event.clientY - startY;
        const distance = Math.sqrt(dx * dx + dy * dy);
        if (distance > 2) {
          return;
        }

        const raycaster = new THREE.Raycaster();
        const mouse = new THREE.Vector2();
        const rect = this.container.getBoundingClientRect();
        mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
        mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;
        raycaster.setFromCamera(mouse, this.camera);

        for (const i in this.groupList) {
          for (const j in this.groupList[i].children) {
            if (this.groupList[i].children[j].name == '单击线') {
              this.groupList[i].remove(this.groupList[i].children[j]);
            }
          }
        }

        // console.log(this.groupList);

        var intersects = raycaster.intersectObjects(this.groupList, true);

        if (intersects.length > 0) {
          console.log(intersects[0]);
          let parent = intersects[0].object.parent;
          this.currentObj = intersects[0].object.parent;

          for (const i in parent.children) {
            if (
              parent.children[i].geometry.constructor == THREE.Geometry ||
              parent.children[i].geometry.constructor == THREE.BufferGeometry
            ) {
              const edgeGeometry = new THREE.EdgesGeometry(parent.children[i].geometry);
              const edgeMaterial = new THREE.LineBasicMaterial({ color: 0xff0000, linewidth: 2 });
              const edgeLines = new THREE.LineSegments(edgeGeometry, edgeMaterial);
              edgeLines.position.set(
                parent.children[i].position.x,
                parent.children[i].position.y,
                parent.children[i].position.z
              );
              edgeLines.scale.set(parent.children[i].scale.x, parent.children[i].scale.y, parent.children[i].scale.z);
              edgeLines.rotation.y = parent.children[i].rotation.y;
              edgeLines.rotation.x = parent.children[i].rotation.x;
              edgeLines.layers.set(0);
              edgeLines.name = '单击线';
              parent.add(edgeLines);
              // let axesHelper = new THREE.AxesHelper(5); // 5是坐标轴的长度
              // axesHelper.position.copy(intersects[0].point);
              // parent.add(axesHelper);
            }
          }
        }
        if (intersects.length > 0 && event.button == 2) {
          event.preventDefault();
          // console.log(this.contextMenu);
          this.contextMenuVisible = true;
          // this[this.contextMenu[0].methods]();
          this.$nextTick(() => {
            let div = document.getElementById('threejs-contextMenu');
            div.style.left = `${event.clientX + 3}px`;
            div.style.top = `${event.clientY + 3}px`;
          });
        }
      });
    },
    createFloor() {
      var floorGeometry = new THREE.BoxGeometry(300, 200, 1);
      var floorMaterial = new THREE.MeshLambertMaterial({ color: 0x666655 });
      var floor = new THREE.Mesh(floorGeometry, floorMaterial);
      floor.position.y = 0;
      floor.rotation.x = Math.PI / 2;
      floor.name = '地板';
      floor.receiveShadow = true;
      this.scene.add(floor);
    },
    createBox(width, height, depth, x, y, z, rote = 0, item) {
      // let outsideGeometry = new THREE.BoxGeometry(width, height, depth);
      // let outside = new THREE.Mesh(outsideGeometry, new THREE.MeshPhongMaterial());
      // outside.position.set(x, y, z);

      // let insideGeometry = new THREE.BoxGeometry(width - 1, height - 1, depth - 1);
      // let inside = new THREE.Mesh(insideGeometry, new THREE.MeshPhongMaterial());
      // inside.position.set(x + 0.5, y, z);

      // let outsideBSP = new ThreeBSP(outside);
      // let insideBSP = new ThreeBSP(inside);
      // var boxBSP = outsideBSP.subtract(insideBSP).toMesh();
      // boxBSP.material = new THREE.MeshPhongMaterial({
      //   color: 0x000000,
      //   specular: 0x555555,
      //   shininess: 30,
      //   metal: true
      // });
      // boxBSP.castShadow = true;
      // boxBSP.name = '柜体';

      // let doorGeometry = new THREE.BoxGeometry(0.1, height - 1, depth - 1);
      // let door = new THREE.Mesh(
      //   doorGeometry,
      //   new THREE.MeshStandardMaterial({
      //     color: 0x777777,
      //     transparent: true,
      //     opacity: 0.5,
      //     refractionRatio: 0.18,
      //     side: THREE.DoubleSide,
      //     metalness: 0,
      //     roughness: 0.1
      //   })
      // );
      // door.position.set(x + width / 2 - 0.1, y, z);
      // door.castShadow = true;
      // door.name = '柜门';

      // let group = new THREE.Group(); // 创建一个画布
      // group.castShadow = true;
      // group.add(boxBSP);
      // group.add(door);
      // group.rotation.y += rote * Math.PI; //-逆时针旋转,+顺时针
      // this.scene.add(group);
      // this.groupList.push(group);

      const loader = new GLTFLoader();
      // for (const i in item) {
      //   let group = new THREE.Group(); // 创建一个画布
      //   group.castShadow = true;
      //   group.position.set(item[i].position.x, item[i].position.y, item[i].position.z);
      //   group.scale.set(item[i].scale.x, item[i].scale.y, item[i].scale.z);
      //   group.rotation.set(item[i].rotation._x, item[i].rotation._y, item[i].rotation._z);
      //   for (const j in item[i].children) {
      //     loader.load(
      //       `${item[i].children[j].path}`, // 替换为你的GLB模型路径
      //       (gltf) => {
      //         const model = gltf.scene.children[0].children[0];
      //         model.position.set(
      //           item[i].children[j].position.x,
      //           item[i].children[j].position.y,
      //           item[i].children[j].position.z
      //         );
      //         model.scale.set(item[i].children[j].scale.x, item[i].children[j].scale.y, item[i].children[j].scale.z);
      //         model.rotation.set(
      //           item[i].children[j].rotation._x,
      //           item[i].children[j].rotation._y,
      //           item[i].children[j].rotation._z
      //         );
      //         model.castShadow = true;
      //         model.path = item[i].children[j].path;
      //         model.name = item[i].children[j].name;
      //         group.add(model);
      //         this.scene.add(group);
      //         this.groupList.push(group);
      //       },
      //       (p) => {
      //         console.log(p);
      //       },
      //       (error) => {
      //         console.error(error);
      //       }
      //     );
      //   }
      // }
      let group = new THREE.Group(); // 创建一个画布
      loader.load(
        `${item.path}`, // 替换为你的GLB模型路径
        (gltf) => {
          const model = gltf.scene.children[0].children[0];
          console.log(gltf);
          console.log(model);
          model.position.set(item.position.x, item.position.y, item.position.z);
          model.scale.set(item.scale.x, item.scale.y, item.scale.z);
          model.rotation.set(item.rotation._x, item.rotation._y, item.rotation._z);
          model.castShadow = true;
          model.path = item.path;
          model.name = item.name;
          group.add(model);
          this.scene.add(group);
          this.groupList.push(group);
        },
        (p) => {
          console.log(p);
        },
        (error) => {
          console.error(error);
        }
      );
    },
    createWall(width, height, depth, angle, x, y, z) {
      var cubeGeometry = new THREE.BoxGeometry(width, height, depth);
      var cube = new THREE.Mesh(
        cubeGeometry,
        new THREE.MeshStandardMaterial({ color: 0x000000, opacity: 0.5, transparent: true })
      );
      cube.position.x = x;
      cube.position.y = y;
      cube.position.z = z;
      cube.rotation.y += angle * Math.PI; //-逆时针旋转,+顺时针
      cube.castShadow = true;
      cube.name = `墙${x}+${y}`;

      this.scene.add(cube);
    },
    startAnimate() {
      this.animate = window.requestAnimationFrame(this.startAnimate);
      this.renderer.render(this.scene, this.camera);
    },
    stopAnimate() {
      cancelAnimationFrame(this.animate);
    }
  },
  beforeDestroy() {
    this.stopAnimate();
  }
};
</script>

<style scoped>
#threejs-container {
  width: 100%;
  height: 100%;
  background: #ffff00;
}

#threejs-contextMenu {
  position: absolute;
  left: 100px;
  top: 100px;
  border-radius: 1px;
  z-index: inherit;
  background: #fff;
  box-shadow: 3px 3px 4px rgb(60 60 60);
  border: solid 1px #292929;
}
.item {
  width: 130px;
  cursor: pointer;
  padding: 5px 25px;
  user-select: none;
  min-width: 100px;
  text-align: left;
  margin: 6px 0;
  border-bottom: solid 1px #f2f2f2;
}

.item:hover {
  background: #f2f2f2;
}

.item:last-child {
  border: none;
}
</style>
